#include <pcap.h>

#include "core/device_manager.h"
#include "core/pcap_device.h"
#include "core/gateway_config.h"
#include "utils/logger.h"

DeviceManager& DeviceManager::getInstance() {
    static DeviceManager instance;
    return instance;
}

bool DeviceManager::initialize() {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_if_t* alldevs;

    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        LOG_ERROR("Error finding devices: " << errbuf);
        return false;
    }

    m_availableDevices.clear();
    for (pcap_if_t* d = alldevs; d; d = d->next) {
        if (d->name) {
            m_availableDevices.emplace_back(d->name);
            LOG_DEBUG("Found available device: " << d->name);
        }
    }

    pcap_freealldevs(alldevs);
    LOG_INFO("Found " << m_availableDevices.size() << " available devices.");
    return true;
}

bool DeviceManager::initialize(const GatewayConfig& config) {
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_if_t* alldevs;

    if (!config.validate()) {
        LOG_ERROR("Invalid configuration. Did not create any device.");
        return initialize();
    }

    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        LOG_ERROR("Error finding devices: " << errbuf);
        return false;
    }
    m_availableDevices.clear();
    for (pcap_if_t* d = alldevs; d; d = d->next) {
        std::string name(d->name);
        m_availableDevices.emplace_back(name);
        for (auto& conf : config.deviceConfigs()) {
            if (conf.name == name) {
                auto device = createDevice(name);
                if (conf.buffer_size > 0) {
                    device->setBufferSize(conf.buffer_size);
                }
                if (conf.timeout_ms > 0) {
                    device->setTimeout(conf.timeout_ms);
                }
                if (conf.promiscuous) {
                    device->setPromiscuousMode(true);
                }
                if (!conf.filter.empty()) {
                    // device->setFilter(conf.filter);
                }
                m_devices[name] = device;
                LOG_INFO("Set device: " << name << " success.");
            }
        }
    }
    bool success = true;
    for (auto& conf : config.deviceConfigs()) {
        if (!m_devices.count(conf.name)) {
            LOG_WARNING("Set device: " << conf.name << " failed! This device is not exist.");
            success = false;
        }
    }
    return success;
}

std::shared_ptr<IDevice> DeviceManager::createDevice(const std::string& deviceName) {
    // check the device
    if (std::find(m_availableDevices.begin(), m_availableDevices.end(), deviceName) == m_availableDevices.end()) {
        LOG_ERROR("Device not available: " << deviceName);
        return nullptr;
    }

    auto it = m_devices.find(deviceName);
    if (it != m_devices.end()) {
        LOG_WARNING("Device already created: " << deviceName);
        return it->second;
    }

    // create device
    // The constructor of PcapDevice is private, but DeviceManager is a friend class.
    auto device = createSpecificDevice<PcapDevice>();
    if (device->open(deviceName)) {
        m_devices[deviceName] = device;
        LOG_INFO("Created device: " << deviceName);
        return device;
    }

    LOG_ERROR("Failed to create device: " << deviceName);
    return nullptr;
}

std::shared_ptr<IDevice> DeviceManager::getOrCreateDevice(const std::string& deviceName) {
    if (m_devices.count(deviceName)) {
        return m_devices[deviceName];
    }
    return createDevice(deviceName);
}

bool DeviceManager::removeDevice(const std::string& deviceName) {
    auto it = m_devices.find(deviceName);
    if (it != m_devices.end()) {
        it->second->close();
        m_devices.erase(it);
        LOG_INFO("Removed device: " << deviceName);
        return true;
    }

    LOG_WARNING("Cannot remove device - not found: " << deviceName);
    return false;
}

std::vector<std::string> DeviceManager::listAvailableDevices() const {
    std::vector<std::string> devices;
    pcap_if_t* alldevs;

    char errbuf[PCAP_ERRBUF_SIZE];
    if (pcap_findalldevs(&alldevs, errbuf) == -1) {
        LOG_ERROR("Error finding devices: " << errbuf);
        return devices;
    }

    for (pcap_if_t* d = alldevs; d; d = d->next) {
        if (d->name) {
            devices.push_back(d->name);
        }
    }

    pcap_freealldevs(alldevs);
    return devices;
}

std::vector<std::string> DeviceManager::listInitializedDevice() const {
    std::vector<std::string> devices;
    for (const auto& [k,_] : m_devices) {
        devices.push_back(k);
    }
    return devices;
}

bool DeviceManager::autoCreateAllDevices() {
    bool success = true;
    for (const auto& deviceName : m_availableDevices) {
        if (!createDevice(deviceName)) {
            LOG_WARNING("Failed to auto-create device: " << deviceName);
            success = false;
        }
    }
    return success;
}